/*
 * Copyright (c) 2020-2021 https://gitee.com/fsfzp888/UVCCapture
 * All rights reserved
 */

#ifndef WEBCAM_WINDOW_HXX
#define WEBCAM_WINDOW_HXX

#include <QImage>
#include <QMainWindow>
#include <QMutex>
#include <QSettings>
#include <QTextToSpeech>
#include <QThread>
#include <QTime>

#include <chrono>
#include <vector>

#include "AVILib.h"
#include "ImageFormats.h"

class QLabel;
class VideoDevice;
class VideoCapture;
class QHBoxLayout;
class QVBoxLayout;
class QPushButton;
class QStatusBar;
class QSpinBox;
class QLineEdit;
class QComboBox;
class QGroupBox;
class QSplitter;

class WebcamWindow;

class StillImageWorker : public QObject
{
    Q_OBJECT
  protected:
    WebcamWindow *m_win;
    std::vector<QImage> m_stillImageQueue;
    QMutex m_stillImageQueueMtx;
    void writeQImageToFile(QImage &img);

  public:
    StillImageWorker(WebcamWindow *win);
    ~StillImageWorker() noexcept;
  public slots:
    void postImage(QImage &img);
    void handleStillImageQueue();
  signals:
    void beginRecordVideo();
    void finishRecordVideo();
    void sendStatusBarMessage(QString str);
    void sendSpeakMessage(QString str);
    void trackExtraImage(int cnt);
};

/**
 * @brief Main window class
 */
class WebcamWindow : public QMainWindow
{
    Q_OBJECT

  public:
    /**
     * @brief Constructor
     *
     * @param parent
     */
    WebcamWindow(QWidget *parent = nullptr);

    /**
     * @brief Destructor
     */
    virtual ~WebcamWindow();

    /**
     * @brief Process new video frame
     *
     * @param data
     * @param len
     * @param device
     */
    void processFrame(const unsigned char *data, int len, VideoDevice *device);

    /**
     * @brief Process new still frame
     *
     * @param data
     * @param len
     * @param device
     */
    void processStillFrame(const unsigned char *data, int len, VideoDevice *device);

    void writeQImageToFile(QImage &img);

    inline void setAppDirPath(QString dir_path) noexcept { m_appDirPath = dir_path; }
    inline bool isRecordingVideo() const noexcept { return m_isRecordingVideo; }
    inline const QString &getAppDirPath() const noexcept { return m_appDirPath; }
    QString getUserName() const;
    int getTimeRange() const;
    inline double GetFPS() const { return m_fps; }

  protected:
    void resizeEvent(QResizeEvent *ev) override;

  signals:
    void beginRecordVideo();
    void finishRecordVideo();
    void sendStatusBarMessage(QString str);
    void sendSpeakMessage(QString str);
    void sendHandleStillImageMessage(QImage &img);

  private slots:
    void presentFrame();
    /**
     * @brief Change video resolution
     *
     * @param int resolution number
     */
    void changeResolution(int resolutionNum);
    /**
     * @brief Change video device
     *
     * @param int device number
     */
    void changeDevice(int deviceNum);
    void startCapture();
    void stopCapture();
    void startRecordVideo();
    void onClickStartRecordVideoButton();
    void stopRecordVideo();
    void onClockStopRecordVideoButton();
    void incCaptureCntCustom(int cnt);
    void incCaptureCnt();
    void incStillCaptureCnt(int cnt);
    void incThreeCaptureCnt();
    void handleFinishRecordVideo();
    void showStatusBarMessage(QString str);
    void speakMessage(QString str);
    void browse();

  private:
    QThread m_stillThread;
    StillImageWorker m_stillWorker;
    QLabel *m_viewport;                     //!< Viewport
    QMutex m_frameMutex;                    //!< Frame mutex
    QMutex m_videoMutex;                    //!< Video mutex
    QImage m_frame;                         //!< Current frame
    QVBoxLayout *m_controlLayout;           //!< Layout for control widgets
    QGroupBox *m_controlGroup;              //!< Group of control widgets
    QHBoxLayout *m_windowLayout;            //!< Main layout
    QGroupBox *m_windowGroup;               //!< Main group
    QPushButton *m_startButton;             //!< Start button
    QPushButton *m_stopButton;              //!< Stop button
    QPushButton *m_captureButton;           //!< Software capture button
    QPushButton *m_captureThreeButton;      //!< Software capture three photo button
    QPushButton *m_startRecordVideoButton;  //!< Start record video button
    QPushButton *m_stopRecordVideoButton;   //!< Stop record video button
    QLabel *m_devicesLabel;
    QComboBox *m_devices;  //!< List of available devices
    QLabel *m_resolutionsLabel;
    QComboBox *m_resolutions;  //!< List of available resolutions
    QLabel *m_directoryLabel;
    QLineEdit *m_directory;                 //!< Output directory
    QPushButton *m_browserButton;           //!< Browse button
    QLabel *m_nameLabel;                    //!< Name label
    QLineEdit *m_name;                      //!< User name input
    QLabel *m_timeRangeLabel;               //!< Hardware trigger time range label
    QSpinBox *m_timeRange;                  //!< Hardware trigger time range
    QHBoxLayout *m_browserDirectoryLayout;  //!< file directory layout
    QGroupBox *m_devicesGroup;              //!< Devices group
    QVBoxLayout *m_devicesLayout;           //!< Devices layout
    QSplitter *m_vsplitter;                 //!< Vertical splitter
    QStatusBar *m_statusBar;                //!< Main window status bar
    VideoCapture *m_videoCapture;           //!< Video controller
    QString m_appDirPath;                   //!< Dir path
    QImageMaker m_makeQImage;               //!< QImage converter
    AVIWriter m_aviWriter;                  //!< AVI video writer
    QSettings m_settings;                   //!< Current settings
    std::vector<QImage> m_stillImageQueue;  //!< Still image stack
    QMutex m_stillImageQueueMtx;            //!< Mutex to protect handling still image stack
    QTextToSpeech *m_textSpeaker;
    QTime m_timer;
    double m_fps;
    int m_handleCount;
    int m_photoCount;         //!< Photo capture counter
    int m_stillPhotoCount;    //!< Still photo capture counter
    bool m_isCapturing;       //!< Capturing flag
    bool m_isStop;            //!< Is stop flag
    bool m_isRecordingVideo;  //!< Is recording video
};

#endif  // WEBCAM_WINDOW_HXX
